package com.gatewaydy.gatwayhandler;

import com.alibaba.fastjson.JSON;
import com.gatewaydy.entity.GatewayInfo;
import com.gatewaydy.service.GatewayService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;

import java.util.List;

/**
 * @author: LCG
 * @date: 2022-10-27 12:24:44
 * @description: 动态路由的处理类，在这个类中通过实现ApplicationRunner接口的run方法，在gateway项目启动的时候
 * 从数据库读取 路由信息然后添加到 gateway服务的内存中，同时如果对路由进行curd操作的时候先更新持久化的信息然后再更新
 * gateway服务中的路由信息，通过gateway提供的RefreshRoutesEvent事件刷新gateway内存中的路由信息
 **/
@Component
@Slf4j
public class GatewayServiceHandler implements ApplicationEventPublisherAware, ApplicationRunner {
    @Autowired
    private GatewayService gatewayService;

    private RouteDefinitionWriter routeDefinitionWriter;

    private ApplicationEventPublisher applicationEventPublisher;

    public GatewayServiceHandler(RouteDefinitionWriter routeDefinitionWriter) {
        this.routeDefinitionWriter = routeDefinitionWriter;
    }

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
       this.applicationEventPublisher=applicationEventPublisher;
    }

    /**
     * @author: LCG
     * @date: 2022-10-27 13:31:26
     * @description:  添加路由信息
     */
    public String addRoutes(RouteDefinition routeDefinition){
        //保存路由信息到数据库
        int i = gatewayService.saveRoute(routeDefinition);
        if(i==1){
            //存入内存中
            routeDefinitionWriter.save(Mono.just(routeDefinition)).subscribe();
            log.info("---存入-gateway服务的缓存中-{}",routeDefinition);
            //刷新gateway的缓存
            this.applicationEventPublisher.publishEvent(new RefreshRoutesEvent(this));
            log.info("---存入-gateway服务的缓存中-执行事件刷新");
            return "ok";
        }else{
            log.info("---存入-db和gateway缓存失败");
            return "faile";
        }

    }


    /**
     * @author: LCG
     * @date: 2022-10-27 14:14:47
     * @description:  删除路由信息
     */
    public String deleteRoute(String serviceId,String groupInfo){
        //从数据中删除
        int i = gatewayService.deleteRoutes(serviceId, groupInfo);
        log.info("从数据库删除路由信息={}",i);
        if(i>0){
            //从内存中清除
            log.info("删除当前服务中的路由信息=start");
            routeDefinitionWriter.delete(Mono.just(serviceId)).subscribe();
            this.applicationEventPublisher.publishEvent(new RefreshRoutesEvent(this));
            log.info("删除当前服务中的路由信息=end");
        }

        return "ok";
    }

    /**
     * @author: LCG
     * @date: 2022-10-27 14:26:56
     * @description:  更新路由信息
     */
    public String updateRoute(RouteDefinition routeDefinition){

        //更新数据库中的路有数据
        gatewayService.saveRoute(routeDefinition);
        //更新当前服务中的路由数据
        routeDefinitionWriter.delete(Mono.just(routeDefinition.getId())).subscribe();
        routeDefinitionWriter.save(Mono.just(routeDefinition)).subscribe();
        this.applicationEventPublisher.publishEvent(new RefreshRoutesEvent(this));

        return "ok";
    }


    /** 
     * @author: LCG
     * @date: 2022-10-27 12:42:43
     * @description:  容器启动的时候加载持久化的路由数据
     */
    @Override
    public void run(ApplicationArguments args) throws Exception {
        log.info("gateway启动加载数据中的路由信息");
        List<GatewayInfo> gatewayInfoList = gatewayService.getGatewayInfoList("g1");
        log.info("查询到的gateway路由数据={}",gatewayInfoList);
        if(gatewayInfoList!=null){
            for (GatewayInfo gatewayInfo : gatewayInfoList) {
                RouteDefinition routeDefinition = JSON.parseObject(gatewayInfo.getGatewayInfo(), RouteDefinition.class);
                routeDefinitionWriter.save(Mono.just(routeDefinition)).subscribe();
                this.applicationEventPublisher.publishEvent(new RefreshRoutesEvent(this));
            }
        }
    }
}
